package com.demo4;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.ApplicationInfo;
import com.github.unidbg.linux.android.dvm.api.ClassLoader;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.linux.android.dvm.wrapper.DvmBoolean;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.util.HashMap;

public class TBSecurity extends AbstractJni implements IOResolver {
    @Override
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {
        System.out.println("file open:"+pathname);
        if(pathname.equals("/data/app/com.taobao.idlefish-ir0oDGsnyfwv5mQUi9cxiw==/base.apk")){
            return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/demo4/apk/fleaMarket_10026251.apk"), pathname));
        }
        return null;
    }

    private final AndroidEmulator emulator;
    private final DvmClass JNICLibrary;
    private final VM vm;
    private long slot;

    public TBSecurity() {
        emulator = AndroidEmulatorBuilder
                .for64Bit()
                .addBackendFactory(new Unicorn2Factory(true))
                .setProcessName("com.taobao.idlefish")
                .setRootDir(new File("unidbg-android/src/test/resources/demo4/rootfs"))
                .build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/demo4/apk/fleaMarket_10026251.apk"));

        emulator.getBackend().registerEmuCountHook(100000); // 设置执行多少条指令切换一次线程
        emulator.getSyscallHandler().setVerbose(true);
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);

        vm.setJni(this);
        vm.setVerbose(true);

        JNICLibrary = vm.resolveClass("com.taobao.wireless.security.adapter.JNICLibrary");

        emulator.getSyscallHandler().addIOResolver(this);
        DalvikModule dm = vm.loadLibrary("sgmainso-6.5.95", true);
        dm.callJNI_OnLoad(emulator);

        vm.addNotFoundClass("com/uc2/crashsdk/JNIBridge");
    }

    // 10101
    //args[3] String value: com.taobao.idlefish.TaoBaoApplication@bf968f2
    //args[3] String value: 3
    //args[3] String value:
    //args[3] String value: /data/user/0/com.taobao.idlefish/app_SGLib
    //args[3] String value:
    public void callinit(){
        DvmObject<?> MapApplication = vm.resolveClass("com.taobao.idlefish.TaoBaoApplication", vm.resolveClass("android/content/Context")).newObject("taobao");
        DvmObject arr = new ArrayObject(
                MapApplication,
                DvmInteger.valueOf(vm, 3),
                new StringObject(vm, ""),
                new StringObject(vm, "/data/user/0/com.taobao.idlefish/app_SGLib"),
                new StringObject(vm, "")
        );
        JNICLibrary.callStaticJniMethodObject(emulator,
                "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;", 10101, arr);
    }

    public void callsgmaininit(){
        JNICLibrary.callStaticJniMethodObject(emulator,
                "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
                10102,
                new ArrayObject(
                        new StringObject(vm, "main"),
                        new StringObject(vm, "6.5.95"),
                        new StringObject(vm, "/data/app/com.taobao.idlefish-ir0oDGsnyfwv5mQUi9cxiw==/lib/arm64/libsgmainso-6.5.95.so")
                ));
    }

    public void callsecuritybody(){
        DalvikModule securitybody = vm.loadLibrary("sgsecuritybodyso-6.5.101", true);
        securitybody.callJNI_OnLoad(emulator);
        JNICLibrary.callStaticJniMethodObject(emulator,
                "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
                10102,
                new ArrayObject(
                        new StringObject(vm, "securitybody"),
                        new StringObject(vm, "6.5.101"),
                        new StringObject(vm, "/data/app/com.taobao.idlefish-ir0oDGsnyfwv5mQUi9cxiw==/lib/arm64/libsgsecuritybodyso-6.5.101.so")
                ));
    }

    public void callmiddlebody(){
        DalvikModule securitybody = vm.loadLibrary("sgmiddletierso-6.5.88", true);
        securitybody.callJNI_OnLoad(emulator);
        JNICLibrary.callStaticJniMethodObject(emulator,
                "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
                10102,
                new ArrayObject(
                        new StringObject(vm, "securitybody"),
                        new StringObject(vm, "6.5.88"),
                        new StringObject(vm, "/data/app/com.taobao.idlefish-ir0oDGsnyfwv5mQUi9cxiw==/lib/arm64/libsgmiddletierso-6.5.88.so")
                ));
    }


    public void callTarget(){
        ArrayObject arr = new ArrayObject(
                new StringObject(vm, "21407387"),
                new StringObject(vm, "X7JT5xF7wsMDAItqMvZBCWXO&&&21407387&99914b932bd37a50b983c5e7c90ae93b&1666221390&mtop.common.gettimestamp&*&&10026251@fleamarket_android_7.7.31&AjuaEhjxkxT7WGSw9lBTyZAs8ShfElfxSetzJ_jl7nu2&&&openappkey=DEFAULT_AUTH&27&&&&&&&"),
                DvmBoolean.valueOf(vm, Boolean.FALSE),
                DvmInteger.valueOf(vm,0),
                new StringObject(vm, "mtop.common.gettimestamp"),
                new StringObject(vm, ""),
                new StringObject(vm, ""),
                new StringObject(vm, ""),
                new StringObject(vm, ""),
                new StringObject(vm, ""),
                DvmInteger.valueOf(vm,0),
                DvmInteger.valueOf(vm,0)
        );
        DvmObject ret = JNICLibrary.callStaticJniMethodObject(emulator,
                "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
                70102, arr
        );
        System.out.println("result:"+ret.getValue().toString());
    }

    public static void main(String[] args) {
        TBSecurity sc = new TBSecurity();
        sc.callinit();
        sc.callsgmaininit();
        sc.callsecuritybody();
        sc.callmiddlebody();
        sc.callTarget();
    }


    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "com/alibaba/wireless/security/mainplugin/SecurityGuardMainPlugin->getMainPluginClassLoader()Ljava/lang/ClassLoader;":{
                return new ClassLoader(vm, null);
            }
            case "com/taobao/wireless/security/adapter/common/SPUtility2->readFromSPUnified(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;":{
                String arg1 = varArg.getObjectArg(0).getValue().toString();
                String arg2 = varArg.getObjectArg(1).getValue().toString();
                System.out.println("readFromSPUnified arg1 "+arg1+" arg2:"+arg2);
                if(arg1.equals("dynamicreid")){
                    return new StringObject(vm, "496b13e0e5717ca");
                }
                if(arg1.equals("dynamicrsid")){
                    return new StringObject(vm, "9f6986925f218be");
                }
                if(arg2.equals("ac7123c301ca455b")){
                    return new StringObject(vm, "1666187720");
                }
                if(arg2.equals("fa641a486784d59b")){
                    return null;
                }
                if(arg1.equals("LOCAL_DEVICE_INFO")){
                    return new StringObject(vm, "X7JT5xF7wsMDAItqMvZBCWXO");
                }

            }
            case "com/taobao/wireless/security/adapter/datacollection/DeviceInfoCapturer->doCommandForString(I)Ljava/lang/String;":{
                int cmd = varArg.getIntArg(0);
                System.out.println(cmd);
                if(cmd == 122){
                    return new StringObject(vm, "com.taobao.idlefish");
                }
                if(cmd == 135){
                    return new StringObject(vm, "X7JT5xF7wsMDAItqMvZBCWXO");
                }
                if(cmd == 123){
                    return new StringObject(vm, "7.7.31");
                }
            }
            case "java/lang/Thread->currentThread()Ljava/lang/Thread;":
                return vm.resolveClass("java/lang/Thread").newObject(Thread.currentThread());
            case "com/alibaba/wireless/security/securitybody/SecurityBodyAdapter->doAdapter(I)Ljava/lang/String;":{
                int key = varArg.getIntArg(0);
                System.out.println("doAdapter arg >>> " + key);
                switch (key) {
                    case 8:
                        return new StringObject(vm, "1");
                    case 6:
                        return new StringObject(vm, "100");
                    case 9:
                        return new StringObject(vm, "1637828058");
                    default:
                        break;
                }
            }
            case "com/alibaba/wireless/security/securitybody/SecurityGuardSecurityBodyPlugin->getPluginClassLoader()Ljava/lang/ClassLoader;":{
                return new ClassLoader(vm, null);
            }
            case "com/taobao/dp/util/CallbackHelper->getInstance()Lcom/taobao/dp/util/CallbackHelper;":{
                return vm.resolveClass("com/taobao/dp/util/CallbackHelper").newObject(null);
            }
            case "com/alibaba/wireless/security/securitybody/InvocationHandlerAdapter->getClassLoader()Ljava/lang/ClassLoader;":{
                return new ClassLoader(vm, null);
            }
            // 这里应该被处理
            case "com/alibaba/one/android/sdk/OneMain->play(IIILjava/lang/Object;)Ljava/lang/Object;":{
                return null;
            }
            case "java/lang/Class->forName(Ljava/lang/String;)Ljava/lang/Class;":{
                String clazz = varArg.getObjectArg(0).getValue().toString();
                System.out.println("class ForName:"+clazz);
                return vm.resolveClass(clazz);
            }
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature){
            case "com/taobao/idlefish/TaoBaoApplication->getPackageCodePath()Ljava/lang/String;":{
                return new StringObject(vm, "/data/app/com.taobao.idlefish-ir0oDGsnyfwv5mQUi9cxiw==/base.apk");
            }
            case "com/taobao/idlefish/TaoBaoApplication->getFilesDir()Ljava/io/File;":{
                return vm.resolveClass("java/io/File").newObject("/data/data/com.taobao.idlefish/files");
            }
            case "java/io/File->getAbsolutePath()Ljava/lang/String;":{
                return new StringObject(vm, dvmObject.getValue().toString());
            }
            case "com/taobao/idlefish/TaoBaoApplication->getApplicationInfo()Landroid/content/pm/ApplicationInfo;":{
                return new ApplicationInfo(vm);
            }
            case "com/taobao/idlefish/TaoBaoApplication->getPackageName()Ljava/lang/String;":{
                return new StringObject(vm, vm.getPackageName());
            }
            case "java/lang/ClassLoader->findClass(Ljava/lang/String;)Ljava/lang/Class;":{
                String clazz = varArg.getObjectArg(0).getValue().toString();
                System.out.println("findClass:"+clazz);
                return vm.resolveClass(clazz);
            }
            case "android/content/Context->getFilesDir()Ljava/io/File;":{
                return vm.resolveClass("java/io/File").newObject("/data/data/com.taobao.idlefish/files");
            }
            case "android/content/Intent->setAction(Ljava/lang/String;)Landroid/content/Intent;":{
                String intent = varArg.getObjectArg(0).getValue().toString();
                System.out.println("intentName:"+intent);
                return vm.resolveClass("android/content/Intent").newObject("android.intent.action.MAIN");
            }
            case "android/content/Intent->addCategory(Ljava/lang/String;)Landroid/content/Intent;":{
                return dvmObject;
            }
            case "java/lang/Thread->getStackTrace()[Ljava/lang/StackTraceElement;":
                StackTraceElement[] elements = ((Thread) dvmObject.getValue()).getStackTrace();
                DvmObject<?>[] objs = new DvmObject[elements.length];
                for (int i = 0; i < elements.length; i++) {
                    objs[i] = vm.resolveClass("java/lang/StackTraceElement").newObject(elements[i]);
                }
                return new ArrayObject(objs);
            case "java/lang/StackTraceElement->toString()Ljava/lang/String;":
                StackTraceElement element = (StackTraceElement)dvmObject.getValue();
                return new StringObject(vm, element.getClassName());
            case "com/taobao/idlefish/TaoBaoApplication->getClassLoader()Ljava/lang/ClassLoader;":{
                return new ClassLoader(vm, null);
            }
            case "dalvik/system/PathClassLoader->loadClass(Ljava/lang/String;)Ljava/lang/Class;":{
                String clazz = varArg.getObjectArg(0).getValue().toString();
                System.out.println("loadClass:"+clazz);
                if(clazz.equals("com.ut.mini.UTAnalytics")){
                    return vm.resolveClass(clazz);
                }
            }
            case "java/lang/StackTraceElement->getFileName()Ljava/lang/String;":{
                StackTraceElement stackTraceElement = (StackTraceElement) dvmObject.getValue();
                return new StringObject(vm, stackTraceElement.getFileName());
            }
            case "java/lang/StackTraceElement->getClassName()Ljava/lang/String;":{
                StackTraceElement stackTraceElement = (StackTraceElement) dvmObject.getValue();
                return new StringObject(vm, stackTraceElement.getClassName());
            }
            case "java/lang/StackTraceElement->getMethodName()Ljava/lang/String;":{
                StackTraceElement stackTraceElement = (StackTraceElement) dvmObject.getValue();
                return new StringObject(vm, stackTraceElement.getMethodName());
            }
            case "java/util/HashMap->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;":
                HashMap<Object,Object> value = (HashMap<Object, Object>) dvmObject.getValue();
                Object k = varArg.getObjectArg(0).getValue();
                Object v = varArg.getObjectArg(1).getValue();
                value.put(k, v);
                System.out.println(k);
                System.out.println(v);
                return dvmObject;
            case "dalvik/system/PathClassLoader->findClass(Ljava/lang/String;)Ljava/lang/Class;":{
                String clazzName = varArg.getObjectArg(0).getValue().toString();
                System.out.println("findclass:"+clazzName);
                return vm.resolveClass(clazzName);
            }
            case "java/lang/Class->getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;":{
                return vm.resolveClass("java/lang/reflect/Method").newObject(null);
            }
            case "java/lang/reflect/Method->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;":{
                return null;
            }
            case "android/content/Context->getOpPackageName()Ljava/lang/String;":{
                return new StringObject(vm, vm.getPackageName());
            }
            case "com/taobao/idlefish/TaoBaoApplication->getPackageManager()Landroid/content/pm/PackageManager;":{
                DvmClass clazz = vm.resolveClass("android/content/pm/PackageManager");
                return clazz.newObject(signature);
            }
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        switch (signature){
            case "android/content/pm/ApplicationInfo->nativeLibraryDir:Ljava/lang/String;":{
                return new StringObject(vm, "/data/app/com.taobao.idlefish-ir0oDGsnyfwv5mQUi9cxiw==/lib/arm64");
            }
        }
        return super.getObjectField(vm, dvmObject, signature);
    }

    @Override
    public int callStaticIntMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "com/uc/crashsdk/JNIBridge->registerInfoCallback(Ljava/lang/String;IJI)I":{
                String name = varArg.getObjectArg(0).getValue().toString();
                System.out.println("JNIBridge registerInfoCallback:"+name);
                return 1;
            }
            case "com/taobao/wireless/security/adapter/datareport/DataReportJniBridge->registerAccsListnerBridge()I":{
                return 1;
            }
            case "com/taobao/wireless/security/adapter/datareport/DataReportJniBridge->registerOrangeListenerBridge()I":{
                return 1;
            }
            case "com/taobao/wireless/security/adapter/datareport/DataReportJniBridge->registerWindVaneListenerBridge()I":{
                return 1;
            }
            case "com/alibaba/wireless/security/framework/utils/UserTrackMethodJniBridge->utAvaiable()I":{
                return 0;
            }
            case "com/taobao/wireless/security/adapter/common/SPUtility2->saveToFileUnifiedForNative(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I":{
                return 0;
            }
        }
        return super.callStaticIntMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public boolean callStaticBooleanMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "com/alibaba/wireless/security/framework/ApmMonitorAdapter->isEnableFullTrackRecord()Z":{
                return false;
            }
        }
        return super.callStaticBooleanMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "java/lang/Integer-><init>(I)V":
                int input = varArg.getIntArg(0);
                return DvmInteger.valueOf(vm, input);
            case "com/alibaba/wireless/security/open/SecException-><init>(Ljava/lang/String;I)V":
                return vm.resolveClass("com/alibaba/wireless/security/open/SecException").newObject(signature);
            case "java/util/HashMap-><init>(I)V":
                return vm.resolveClass("java/util/HashMap").newObject(new HashMap<>());
            case "android/content/Intent-><init>()V":{
                return dvmClass.newObject(null);
            }
            case "java/util/HashMap-><init>()V":{
                return ProxyDvmObject.createObject(vm, new HashMap<>());
            }
        }
        return super.newObject(vm, dvmClass, signature, varArg);
    }

    @Override
    public void callStaticVoidMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "com/alibaba/wireless/security/open/edgecomputing/ECMiscInfo->registerAppLifeCyCleCallBack()V":{
                return;
            }
            case "com/alibaba/one/android/sdk/OneMain->initialize(Landroid/content/Context;)V":{
                return;
            }
        }
        super.callStaticVoidMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public long callStaticLongMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            // 自行处理
            case "android/os/SystemClock->elapsedRealtime()J":{
                return 0x10000L;
            }
        }
        return super.callStaticLongMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public int callIntMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature){
            case "java/lang/StackTraceElement->getLineNumber()I":{
                StackTraceElement stackTraceElement = (StackTraceElement) dvmObject.getValue();
                return stackTraceElement.getLineNumber();
            }
        }
        return super.callIntMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public long getStaticLongField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature){
            case "com/alibaba/wireless/security/framework/SGPluginExtras->slot:J":{
                return slot;
            }
        }
        return super.getStaticLongField(vm, dvmClass, signature);
    }

    @Override
    public void setStaticLongField(BaseVM vm, DvmClass dvmClass, String signature, long value) {
        switch (signature){
            case "com/alibaba/wireless/security/framework/SGPluginExtras->slot:J":{
                slot = value;
                return;
            }
        }
        super.setStaticLongField(vm, dvmClass, signature, value);
    }

    @Override
    public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "android/os/Build$VERSION->SDK_INT:I":
                return 29;
        }
        return super.getStaticIntField(vm, dvmClass, signature);
    }
}
